package app

import (
	"errors"
	"log/slog"
	"sync"
	"time"

	"gitee.com/jiebaiyou/demo-grpc/shared/utils"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

// 全局配置
var (
	ConfigFile *string
	Config     *GLOBALCONFIG
)

type GLOBALCONFIG struct {
	NAME       string    `mapstructure:"NAME" yaml:"NAME"`
	PORT       string    `mapstructure:"PORT" yaml:"PORT"`
	DEBUG      bool      `mapstructure:"DEBUG" yaml:"DEBUG"`
	TLS        bool      `mapstructure:"TLS" yaml:"TLS"`
	IP         string    `mapstructure:"IP" yaml:"IP"`
	LOG        *LOG      `mapstructure:"LOG" yaml:"LOG"`
	ETCD       *ETCD     `mapstructure:"ETCD" yaml:"ETCD"`
	MYSQL      *MYSQL    `mapstructure:"MYSQL" yaml:"MYSQL"`
	REDIS      *REDIS    `mapstructure:"REDIS" yaml:"REDIS"`
	RABBITMQ   *RABBITMQ `mapstructure:"RABBITMQ" yaml:"RABBITMQ"`
	WHITE_LIST []string  `mapstructure:"WHITE_LIST" yaml:"WHITE_LIST"`
}

type LOG struct {
	LEVEL        string `mapstructure:"LEVEL" yaml:"LEVEL"`
	JSON_FORMAT  bool   `mapstructure:"JSON_FORMAT" yaml:"JSON_FORMAT"`
	TIME_FORMAT  string `mapstructure:"TIME_FORMAT" yaml:"TIME_FORMAT"`
	ADD_SOURCE   bool   `mapstructure:"ADD_SOURCE" yaml:"ADD_SOURCE"`
	OUTPUT       string `mapstructure:"OUTPUT" yaml:"OUTPUT"`
	PATH         string `mapstructure:"PATH" yaml:"PATH"`
	ROTATE_DAILY bool   `mapstructure:"ROTATE_DAILY" yaml:"ROTATE_DAILY"`
}

type ETCD struct {
	ENDPOINTS []string `mapstructure:"ENDPOINTS" yaml:"ENDPOINTS"`
	USERNAME  string   `mapstructure:"USERNAME" yaml:"USERNAME"`
	PASSWORD  string   `mapstructure:"PASSWORD" yaml:"PASSWORD"`
}

type MYSQL struct {
	HOST     string `mapstructure:"HOST" yaml:"HOST"`
	PORT     int    `mapstructure:"PORT" yaml:"PORT"`
	USERNAME string `mapstructure:"USERNAME" yaml:"USERNAME"`
	PASSWORD string `mapstructure:"PASSWORD" yaml:"PASSWORD"`
	DATABASE string `mapstructure:"DATABASE" yaml:"DATABASE"`
}

type REDIS struct {
	HOST     string `mapstructure:"HOST" yaml:"HOST"`
	PORT     int    `mapstructure:"PORT" yaml:"PORT"`
	USERNAME string `mapstructure:"USERNAME" yaml:"USERNAME"`
	PASSWORD string `mapstructure:"PASSWORD" yaml:"PASSWORD"`
	DB       int    `mapstructure:"DB" yaml:"DB"`
}

type RABBITMQ struct {
	HOST     string `mapstructure:"HOST" yaml:"HOST"`
	PORT     string `mapstructure:"PORT" yaml:"PORT"`
	USERNAME string `mapstructure:"USERNAME" yaml:"USERNAME"`
	PASSWORD string `mapstructure:"PASSWORD" yaml:"PASSWORD"`
	VHOST    string `mapstructure:"VHOST" yaml:"VHOST"`
}

// 初始化配置文件
func InitConfig(configPath string) (config *GLOBALCONFIG, err error) {
	// 如果配置文件未指定，则输出错误信息并退出
	if ConfigFile == nil || *ConfigFile == "" {
		return nil, errors.New("configuration file not specified")
	}

	// 配置文件读取顺序
	viper.AddConfigPath(".") // 查找可执行文件所在的路径
	viper.SetConfigFile(configPath)

	// 解析配置文件
	config, err = ParseConfig()
	if err != nil {
		return nil, err
	}

	// 监听配置文件变动
	// viper.WatchConfig()
	// WatchConfig()

	return
}

// 解析配置文件
func ParseConfig() (config *GLOBALCONFIG, err error) {
	// 读取配置文件
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			return nil, errors.New("configuration file not found")
		} else {
			return nil, errors.New("error reading configuration file: " + err.Error())
		}
	}
	slog.Info("配置文件路径", slog.String("path", viper.ConfigFileUsed()))
	slog.Info("配置文件初始化成功")

	// 反序列化配置文件到 GLOBALCONFIG 结构体
	err = viper.Unmarshal(&config)
	if err != nil {
		return nil, errors.New("error deserializing configuration file: " + err.Error())
	}

	// 获取并设置当前 IP 地址
	config.IP = utils.GetIpAddress()
	Config = config

	return
}

// 监听配置文件变更
func WatchConfig() {
	var (
		mutex          sync.Mutex                   // 互斥锁
		timer          = time.NewTimer(time.Minute) // 计时器
		configWatchDur = time.Second * 3            // 配置文件监控间隔时间
	)
	timer.Stop()

	viper.OnConfigChange(func(e fsnotify.Event) {
		if e.Op == fsnotify.Write {
			mutex.Lock()
			timer.Reset(configWatchDur) // 重置计时器，而不是创建新的
			mutex.Unlock()
		}
	})

	go func() {
		for {
			<-timer.C
			mutex.Lock()
			slog.Info("配置文件已更改")
			if _, err := ParseConfig(); err != nil {
				slog.Error("应用配置文件更改失败", slog.String("error", err.Error()))
			}
			mutex.Unlock()
		}
	}()
}
